import asyncio
import sys
import requests
from netaddr import IPNetwork
from bs4 import BeautifulSoup

Ports_web = [80, 88, 443, 7001, 8000, 8008, 8888, 8080, 8088, 8089, 8161, 9090]
Ports_other = [21, 22, 445, 1100, 1433, 1434, 1521, 3306, 3389, 6379, 8009, 9200,11211, 27017, 50070]

COUNT = 0
TIMEOUT_HTTP = 5
TIMEOUT_SOCK = 0.9
PATH = ''

user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36(KHTML, like Gecko)" \
" Chrome/101.0.4951.41 Safari/537.36"

# 输出字符时的各种颜⾊前缀
pr_purp = '\033[95m'
pr_blue = '\033[94m'
pr_red = '\033[31m'
pr_yellow = '\033[93m'
pr_green = '\033[96m'

# 结束颜⾊
pr_end = '\033[0m'

def tag(info):
    return "[" + info + "]"

def get_info(url, keyword):
    try:

        r = requests.get(url, headers={'UserAgent': user_agent}, timeout=TIMEOUT_HTTP,
        verify=False,allow_redirects=True)

        # 解析html
        soup = BeautifulSoup(r.content, "lxml")

        # HTTP头信息分析
        info_code = tag(pr_red + str(r.status_code) + pr_end)
        info_title = tag(pr_blue + soup.title.string.replace('\n', '').replace('\r','').replace('\t', '') + pr_end) if soup.title else tag("")
        info_len = tag(pr_purp + str(len(r.content)) + pr_end)

        # 获取请求头中的服务器信息
        if 'Server' in r.headers:
            info_server = " [" + pr_yellow + r.headers['Server']
            info_server += " " + r.headers['X-Powered-By'] + pr_end + "]" if 'X-Powered-By' in r.headers else "]"
        else:
            info_server = tag("")

        result = info_code + info_title + info_server + info_len

        # HTTP内容，关键字匹配
        key = tag(pr_red + "Keyword!!!" + pr_end) if keyword and keyword in r.text else ""
        return result + key

    except Exception as e:
    # print(e)
        return tag(pr_green + "open" + pr_end)

async def connet(host, sem, keyword):
    """
    先⽤异步⽹络请求判断端⼝是否存在，如果存在，再对web端⼝进⾏信息输出
    :param host thread keyword ip
    :param sem: 设置进程
    :param keyword: 对web内容关键字匹配
    :param ip: 获取⽬标host的IP
    :return info
    """
    global COUNT
    async with sem:
        for port in Ports_web:
            fut = asyncio.open_connection(host=host, port=port)
            try:
                _, writer = await asyncio.wait_for(fut, timeout=TIMEOUT_SOCK)
                if writer:
                # 如果没有 443/8443 端⼝，则使⽤http协议，否则使⽤https协议
                    protocol = "http" if int(port) not in [443, 8443] else "https"
                    url = "{0}://{1}:{2}{3}".format(protocol, host, port, PATH)
                    info = get_info(url, keyword)
                    sys.stdout.write("%s %s\n" % (url, info))
                    COUNT += 1
            except Exception as e:
                # print(e)
                pass

async def scan():
    # ⽤于并发控制的信号量
    sem = asyncio.Semaphore(60)
    keyword = ""
    c_ip = "192.168.0.1/24"
    # C段IP转换为单个IP的list
    ips = [str(ip) for ip in IPNetwork(c_ip)]
    tasks = []
    for host in ips:
        tasks.append(asyncio.create_task(connet(host, sem, keyword)))
    await asyncio.wait(tasks)

if __name__ == '__main__':
    asyncio.run(scan())